#include "DDR_SSD.h"

DDR_SSD::DDR_SSD(sc_module_name	name_,
				 unsigned		SSD_DMA_PRD_start_addr_,
				 unsigned		Main_SSD_DMA_cmd_reg_start_addr_,
				 unsigned		Main_DMA_check_reg_start_addr_,
				 unsigned		System_type_,
				 unsigned		DRAM_rank_index_,
				 unsigned		DRAM_bank_index_,
				 unsigned		DRAM_row_index_,
				 unsigned		DRAM_col_index_)
	: sc_module							(name_)
	, SSD_DMA_PRD_start_addr			(SSD_DMA_PRD_start_addr_)
	, SSD_DMA_PRD_start_addr_init		(SSD_DMA_PRD_start_addr_)
	, Main_SSD_DMA_cmd_reg_start_addr	(Main_SSD_DMA_cmd_reg_start_addr_)
	, Main_DMA_check_reg_start_addr		(Main_DMA_check_reg_start_addr_)
	, System_type						(System_type_)
	, DRAM_rank_index					(DRAM_rank_index_)
	, DRAM_bank_index					(DRAM_bank_index_)
	, DRAM_row_index					(DRAM_row_index_)
	, DRAM_col_index					(DRAM_col_index_)
	, SSD_DQS							('Z')
	, DRAM_refresh_enable				(false)
	, SSD_DMA_enable					(false)
	, SSD_DMA_PRD_fetched				(false)
	, SSD_DMA_buffered					(false)
	, SSD_DMA_end						(0x0)
	, current_state_1					(T_IDLE)
	, current_state_2					(T_IDLE)
	, LBA_write							(0x0)
	, LBA_read							(0x0)
	, SSD_RD_pIN						(0)
	, SSD_RD_pOUT						(0)
	, SSD_WR_pIN						(0)
	, SSD_WR_pOUT						(0)
	, DMA_SSD_RD_Buffer_pOUT			(0)
	, DMA_SSD_RD_Buffer_pIN				(0)
	, DRAM_RD_pIN						(0)
	, DRAM_RD_pOUT						(0)
	, DRAM_WR_pIN						(0)
	, DRAM_WR_pOUT						(0)
	, DMA_DRAM_RD_Buffer_pOUT			(0)
	, DMA_DRAM_RD_Buffer_pIN			(0)
	, CH1_Buffer_pIN					(0)
	, CH1_Buffer_pOUT					(0)
	, CH2_Buffer_pIN					(0)
	, CH2_Buffer_pOUT					(0)
	, C_WRITE							(false)
	, C_READ							(false)
	, DRAM_or_SSD						(true)
	, SSD_Cache_WR_request				(false)
	, SSD_Cache_RD_request				(false)
	, DRAM_Cache_WR_request				(false)
	, DRAM_Cache_RD_request				(false)
	, SSD_Cache_finished				(true)
	, DRAM_Cache_finished				(true)
	, F_WRITE_1							(false)
	, F_READ_1							(false)
	, F_ERASE_1							(false)
	, F_WRITE_2							(false)
	, F_READ_2							(false)
	, F_ERASE_2							(false)
	, DATA_cnt_1						(0)
	, DATA_cnt_2						(0)
	, DATA_word_1						(0)
	, DATA_word_2						(0)
	, Confirm_commanded_1				(false)
	, Confirm_commanded_2				(false)
	, cache_buffer_read_1				(false)
	, cache_buffer_read_2				(false)
{
	SDRAM_DDR_mode_register_set(0, 0xA62);
	SDRAM_DDR_mode_register_set(1, 0x0);
	SDRAM_DDR_mode_register_set(2, 0x0);

	SC_THREAD(DRAM_timing_control_action);
	dont_initialize();
	sensitive << DRAM_CLK;

	SC_THREAD(Cache_arbiter_action);
	dont_initialize();
	sensitive << DRAM_CLK.pos();

	SC_THREAD(Cache_process);
	dont_initialize();
	sensitive << DRAM_CLK.pos();

	SC_THREAD(FLASHcontroller_process_1);
	dont_initialize();
	sensitive << FLASH_CLK.pos();

	SC_THREAD(FLASHcontroller_process_2);
	dont_initialize();
	sensitive << FLASH_CLK.pos();
}

DDR_SSD::~DDR_SSD()
{
}

const bool
DDR_SSD::SDRAM_refresh(bool	refresh_type_)
{
	SDRAM_precharge(0, 0, true);

	return true;
}

const bool
DDR_SSD::SDRAM_row_activate(unsigned		rank_sel_,
							unsigned		bank_sel_,
							SDRAM_addr_t	row_addr_)
{
	return true;
}

const bool
DDR_SSD::SDRAM_read_command(unsigned		rank_sel_,
							unsigned		bank_sel_,
							SDRAM_addr_t	col_addr_,
							bool			auto_precharge_)
{
	LBA_read_temp = col_addr_;
	if (col_addr_ < (Main_DMA_check_reg_start_addr>>12))
	{
		SSD_Cache_RD_request = true;
	}
	else
	{
		SSD_DQS = '0';
	}

	return true;
}

const bool
DDR_SSD::SDRAM_write_command(unsigned		rank_sel_,
							 unsigned		bank_sel_,
							 SDRAM_addr_t	col_addr_,
							 bool			auto_precharge_)
{
	LBA_write_temp = col_addr_;

	return true;
}

const bool
DDR_SSD::SDRAM_precharge(unsigned	rank_sel_,
						 unsigned	bank_sel_,
						 bool		precharge_all_)
{
	return true;
}

const bool
DDR_SSD::SDRAM_DDR_mode_register_set(unsigned		EMRS_num_,
									 unsigned short	EMRS_code_)
{
	switch(EMRS_num_)
	{
	case 0:
		SSD_mode_register.active_power_down_slow_exit = (EMRS_code_ >> 12) & 0x1;
		SSD_mode_register.write_recovery  = ((EMRS_code_ >> 9) & 0x7) + 1;
		SSD_mode_register.DLL_reset  = (EMRS_code_ >> 8) & 0x1;
		SSD_mode_register.test_mode  = (EMRS_code_ >> 7) & 0x1;
		SSD_mode_register.CAS_latency = (EMRS_code_ >> 4) & 0x7;
		SSD_mode_register.burst_type = (EMRS_code_ >> 3) & 0x1;
		if ((EMRS_code_ & 0x7) != 0)
			SSD_mode_register.burst_length = pow(2, EMRS_code_ & 0x7);
		else
			SSD_mode_register.burst_length = DDR_SSD_FULL_PAGE_BURST_LENGTH;
		break;
	case 1:
		SSD_mode_register.output_buffer_enable = ~((EMRS_code_ >> 12) & 0x1);
		SSD_mode_register.RDQS_enable = (EMRS_code_ >> 11) & 0x1;
		SSD_mode_register.DQS_bar_enable  = ~((EMRS_code_ >> 10) & 0x1);
		SSD_mode_register.OCD_calibration_program  = (EMRS_code_ >> 7) & 0x7;
		SSD_mode_register.additive_latency  = (EMRS_code_ >> 3) & 0x7;
		SSD_mode_register.ODT_resistance = (EMRS_code_ >> 5) & 0x2 | (EMRS_code_ >> 2) & 0x1;
		SSD_mode_register.output_driver_impedance_control = (EMRS_code_ >> 1) & 0x1;
		SSD_mode_register.DLL_enable = ~(EMRS_code_ & 0x1);
		break;
	case 2:
		SSD_mode_register.high_temperature_self_refresh_rate_enable = (EMRS_code_ >> 7) & 0x1;
		SSD_mode_register.partial_array_self_refresh = EMRS_code_ & 0x7;
		break;
	default:
		cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> Unsupported EMRS command." << endl;
		sc_stop();
		break;
	}

	return true;
}

const SDRAM_DDR_data_t
DDR_SSD::SDRAM_DDR_read_data(unsigned	rank_sel_,
							 unsigned	bank_sel_,
							 unsigned	current_burst_,
							 bool		direct_access_)
{
	SDRAM_DDR_data_t return_data;
/*
	if ((SSD_RD_pOUT == 0) && (LBA_read_temp >= (Main_SSD_DMA_cmd_reg_start_addr>>12)))
	{
		SSD_RD_FIFO[SSD_RD_pOUT] = SSD_DMA_end;
		SSD_RD_FIFO[SSD_RD_pOUT+1] = 0x0;
		if (SSD_DMA_end)
			SSD_DMA_PRD_start_addr = SSD_DMA_PRD_start_addr_init;
	}
	else 
*/
	if ((SSD_RD_pOUT == 0) && (LBA_read_temp >= (Main_DMA_check_reg_start_addr>>12)))
	{
		SSD_RD_FIFO[SSD_RD_pOUT] = SSD_write_end & SSD_DMA_end;
		SSD_RD_FIFO[SSD_RD_pOUT+1] = 0x0;
	}

	return_data.lower_bytes = SSD_RD_FIFO[SSD_RD_pOUT++];
	return_data.upper_bytes = SSD_RD_FIFO[SSD_RD_pOUT++];

	if (SSD_RD_pOUT == CRC_BUFFER)
	{
		SSD_RD_pOUT = 0;
		SSD_DQS = 'Z';
	}
	else
	{
		if (SSD_DQS == '0')
		{
			SSD_DQS = '1';
		}
		else if (SSD_DQS == '1')
		{
			SSD_DQS = '0';
		}
		else
		{
			cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> DQS signal is not valid." << endl;
			sc_stop();
		}
	}
	if ((SSD_RD_pOUT == 8) && (LBA_read_temp >= (Main_SSD_DMA_cmd_reg_start_addr>>12)))
	{
		SSD_RD_pOUT = 0;
		SSD_DQS = 'Z';
	}
	else if ((SSD_RD_pOUT == 8) && (LBA_read_temp >= (Main_DMA_check_reg_start_addr>>12)))
	{
		SSD_RD_pOUT = 0;
		SSD_DQS = 'Z';
	}

	return return_data;
}

const bool
DDR_SSD::SDRAM_DDR_write_data(unsigned			rank_sel_,
							  unsigned			bank_sel_,
							  unsigned			current_burst_,
							  SDRAM_DDR_data_t	data_,
							  bool				direct_access_)
{
	if ((SSD_DQS != '0') && (SSD_DQS != '1'))
	{
		cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> DQS signal is not valid." << endl;
		sc_stop();
	}
	SSD_WR_FIFO[SSD_WR_pIN++] = data_.lower_bytes;
	SSD_WR_FIFO[SSD_WR_pIN++] = data_.upper_bytes;
	if ((SSD_WR_pIN == 8) && (LBA_write_temp >= Main_SSD_DMA_cmd_reg_start_addr>>12))
	{
//#ifdef	DEBUG_DDR_SSD
		cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to FIFO is finished...North SSD-DMA enabled." <<endl;
//#endif
		SSD_DMA_command = SSD_WR_FIFO[0];
		if (SSD_DMA_command == 0xC8)
			DRAM_Cache_RD_request = true;
		SSD_DMA_sector_cnt = SSD_WR_FIFO[1];
		SSD_DMA_LBA = SSD_DMA_LBA_current = SSD_WR_FIFO[2];
		SSD_DMA_PRD_length = SSD_WR_FIFO[3];
		SSD_DMA_end = 0x0;
		PRD_current = SSD_DMA_sector_cnt/(4096/512);
		SSD_DMA_enable = true;
		SSD_DMA_enable_time = sc_time_stamp();
		DMA_SSD_RD_Buffer_pIN = DMA_SSD_RD_Buffer_pOUT = 0;
		DMA_DRAM_RD_Buffer_pIN = DMA_DRAM_RD_Buffer_pOUT = 0;
		SSD_WR_pIN = 0;
	}
	else if (SSD_WR_pIN == CRC_BUFFER)
	{
//#ifdef	DEBUG_DDR_SSD
		cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to FIFO is finished...Enable Write to CACHE." <<endl;
//#endif
		SSD_Cache_WR_request = true;
		SSD_WR_pIN = 0;
		SSD_write_end = 0x0;
	}

	return true;
}

const sc_logic
DDR_SSD::SDRAM_DDR_get_DQS() const
{
	return SSD_DQS;
}

const bool
DDR_SSD::SDRAM_DDR_set_DQS(sc_logic	DQS_)
{
	SSD_DQS = DQS_;

	return true;
}

const char*
DDR_SSD::SDRAM_get_state_name(SDRAM_state_t	state_) const
{
	char* state_name = new char[20];

	switch(state_)
	{
	case 0:
		state_name = "SDRAM_POWER_ON";
		break;
	case 1:
		state_name = "SDRAM_IDLE";
		break;
	case 2:
		state_name = "SDRAM_ROW_ACT";
		break;
	case 3:
		state_name = "SDRAM_READ";
		break;
	case 4:
		state_name = "SDRAM_READ_AP";
		break;
	case 5:
		state_name = "SDRAM_WRITE";
		break;
	case 6:
		state_name = "SDRAM_WRITE_AP";
		break;
	case 7:
		state_name = "SDRAM_PRECHARGE";
		break;
	case 8:
		state_name = "SDRAM_MRS";
		break;
	case 9:
		state_name = "SDRAM_AUTO_REF";
		break;
	case 10:
		state_name = "SDRAM_SELF_REF";
		break;
	default:
		state_name = "SDRAM_NULL";
	}

	return state_name;
}

void
DDR_SSD::DRAM_timing_control_action()
{
	const unsigned DRAM_tRCD = DDR_DEFAULT_tRCD;
	const unsigned DRAM_CL = DDR_DEFAULT_CL;
	const unsigned DRAM_BL = DDR_DEFAULT_BURST_LENGTH;
	const unsigned DRAM_tDQSS = DDR_DEFAULT_tDQSS;
	const unsigned DRAM_tWR = DDR_DEFAULT_tWR;
	const unsigned DRAM_tRP = DDR_DEFAULT_tRP;
	const unsigned DRAM_tRFC = DDR_DEFAULT_tRFC;

	unsigned tRFC_cnt = DRAM_tRFC;

	unsigned DRAM_rank_num = pow(2, DRAM_rank_index);
	unsigned DRAM_bank_num = pow(2, DRAM_bank_index);

	SDRAM_bank_interleave_state_t DRAM_curr_state = IDLE_STATE;

	unsigned full_data_length;
	SDRAM_DDR_data_t SDRAM_current_data;
	unsigned full_banks_num;
	unsigned remained_banks_num;
	unsigned row_activate_cnt;
	unsigned start_bank;
	unsigned end_bank;

	SDRAM_DDR_bank_control_info_t* bank_manager;

	bool command_sent;
	bool data_reserved;

	SDRAM_addr_t addr;

	while (true)
	{
		int bank_cnt;

		switch(DRAM_curr_state)
		{
		case IDLE_STATE:
			if ((DRAM_refresh_enable) && (DRAM_CLK == true))
			{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Start Auto Refresh." << endl;
#endif
				DRAM_port->SDRAM_refresh(1, false);
				DRAM_curr_state = REFRESH_STATE;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Now Refreshing..." << tRFC_cnt << endl;
#endif
				tRFC_cnt--;
			}
			else if ((SSD_DMA_enable) && (SSD_DMA_enable_time != sc_time_stamp()) &&
				((!SSD_DMA_buffered && SSD_DMA_command == 0xCA) || (SSD_DMA_buffered && SSD_DMA_command == 0xC8)))
			{
				if ((!SSD_DMA_PRD_fetched && !SSD_DMA_buffered && SSD_DMA_command == 0xCA) || (!SSD_DMA_PRD_fetched && SSD_DMA_buffered && SSD_DMA_command == 0xC8))
				{
					addr = SSD_DMA_PRD_start_addr;
					if ((2*SSD_DMA_PRD_length)%(DRAM_BL*((DDR_EXT_BITWIDTH_MODULE/8)/4)) == 0)
						full_data_length = 2*SSD_DMA_PRD_length;
					else
						full_data_length = (2*SSD_DMA_PRD_length) + ((DRAM_BL*((DDR_EXT_BITWIDTH_MODULE/8)/4))-(2*SSD_DMA_PRD_length)%(DRAM_BL*((DDR_EXT_BITWIDTH_MODULE/8)/4)));
//#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "PRD for North SSD-DMA is requested." << endl;
//#endif
				}
				else
				{
//#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
					if (SSD_DMA_command == 0xC8)
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Send data to main memory"<< endl;
					else
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Request data to main memory"<< endl;
//#endif
					addr = SSD_DMA_base_addr;
					full_data_length = 1024;
				}
				full_banks_num = remained_banks_num = full_data_length / ((DRAM_BL * DDR_EXT_BITWIDTH_MODULE/8) /4);
				row_activate_cnt = 0;
				start_bank = (addr >> 5) & 0x7;
				end_bank = ((((addr + full_data_length*4) >> 5) & 0x7) - 1) % (DRAM_rank_num*DRAM_bank_num);
				bank_manager = new SDRAM_DDR_bank_control_info_t[DRAM_rank_num*DRAM_bank_num];
				for (int i=0;i<DRAM_rank_num*DRAM_bank_num;i++)
				{
					bank_manager[i].bank_curr_state = SDRAM_IDLE;
					bank_manager[i].tRCD_cnt = DRAM_tRCD;
					bank_manager[i].CL_cnt = DRAM_CL;
					bank_manager[i].read_data_enable = false;
					bank_manager[i].BL_cnt = DRAM_BL;
					bank_manager[i].read_auto_precharge_cnt = DRAM_BL;
					bank_manager[i].tDQSS_cnt = DRAM_tDQSS;
					bank_manager[i].tWR_cnt = DRAM_tWR;
					bank_manager[i].tRP_cnt = DRAM_tRP;
					if ((full_banks_num%(DRAM_rank_num*DRAM_bank_num) == 0) && (start_bank == 0))
					{
						bank_manager[i].bank_addr = new SDRAM_addr_t[full_banks_num/(DRAM_rank_num*DRAM_bank_num)];
						bank_manager[i].bank_last = full_banks_num/(DRAM_rank_num*DRAM_bank_num)-1;
					}
					else if (start_bank <= end_bank)
					{
						bank_manager[i].bank_addr = new SDRAM_addr_t[full_banks_num/(DRAM_rank_num*DRAM_bank_num)+1];
						bank_manager[i].bank_last = full_banks_num/(DRAM_rank_num*DRAM_bank_num);
					}
					else
					{
						if (end_bank+1 == start_bank)
						{
							bank_manager[i].bank_addr = new SDRAM_addr_t[full_banks_num/(DRAM_rank_num*DRAM_bank_num)+1];
							bank_manager[i].bank_last = full_banks_num/(DRAM_rank_num*DRAM_bank_num);
						}
						else
						{
							bank_manager[i].bank_addr = new SDRAM_addr_t[full_banks_num/(DRAM_rank_num*DRAM_bank_num)+2];
							bank_manager[i].bank_last = full_banks_num/(DRAM_rank_num*DRAM_bank_num)+1;
						}
					}
					bank_manager[i].bank_first = 0;
				}
				unsigned addr_line = 0;
				for (int i=0;i<full_banks_num;i++)
				{
					if ((i != 0) && ((start_bank+i)%(DRAM_rank_num*DRAM_bank_num) == 0))
						addr_line++;
					bank_manager[(start_bank+i)%(DRAM_rank_num*DRAM_bank_num)].bank_addr[addr_line] = addr + 0x20 * i;
				}
				for (int i=0;i<start_bank;i++)
					bank_manager[i].bank_first++;
				if (end_bank < DRAM_rank_num*DRAM_bank_num-1)
				{
					for (int i=end_bank+1;i<DRAM_rank_num*DRAM_bank_num;i++)
					{
						if (bank_manager[i].bank_last !=0)
							bank_manager[i].bank_last--;
						else
							bank_manager[i].bank_first++;
					}
				}
				if ((!SSD_DMA_PRD_fetched) || (!SSD_DMA_buffered && SSD_DMA_command == 0xCA))
				{
					DRAM_curr_state = READ_STATE;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Start Read to DRAM port" << endl;
#endif
				}
				else
				{
					DRAM_curr_state = WRITE_STATE;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Start Write to DRAM port" << endl;
#endif
				}
				data_reserved = false;
			}
			else
			{
/*
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
				cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> MRS commanded : 0xA62 (burst_length = 4)" << endl;
#endif
				DRAM_port->SDRAM_DDR_mode_register_set(1, 0, 0xA62);
				DRAM_curr_state = EMRS_STATE;
*/
			}
			break;
		case READ_STATE:
			command_sent = false;
			bank_cnt = 0;
			for (int i=start_bank;bank_cnt<8;i=(i+1)%(DRAM_rank_num*DRAM_bank_num))
			{
				if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
				{
					unsigned rank_sel = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 5) & 0x1;
					unsigned bank_sel = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 6) & 0x3;
					SDRAM_addr_t row_addr = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 16) & 0x1FFF;
					SDRAM_addr_t col_addr = ((bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 6) & 0x3FC) | ((bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 3) & 0x3);
					switch (bank_manager[i].bank_curr_state)
					{
					case SDRAM_IDLE:
						if ((DRAM_CLK == true) && (!command_sent) && (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == 'Z'))
						{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Row Activate at Rank=" << hex << rank_sel << ", Bank=" << bank_sel << ", Row=0x" << row_addr << dec << endl;
#endif
							DRAM_port->SDRAM_row_activate(1, rank_sel, bank_sel, row_addr);
							command_sent = true;
							row_activate_cnt++;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Row Activate...tRCD count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRCD_cnt << endl;
#endif
							bank_manager[i].bank_curr_state = SDRAM_ROW_ACT;
							bank_manager[i].tRCD_cnt--;
						}
						break;
					case SDRAM_ROW_ACT:
						if (DRAM_CLK == true)
						{
							if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) != 'Z')
							{
								bank_manager[i].bank_curr_state = SDRAM_IDLE;
								row_activate_cnt--;
								bank_manager[i].tRCD_cnt++;
							}
							else
							{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Row Activate...tRCD count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRCD_cnt << endl;
#endif
								bank_manager[i].tRCD_cnt--;
								if (bank_manager[i].tRCD_cnt == 0)
								{
									bank_manager[i].bank_curr_state = SDRAM_READ_AP;
									bank_manager[i].tRCD_cnt = DRAM_tRCD;
								}
							}
						}
						break;
					case SDRAM_READ_AP:
						if (bank_manager[i].CL_cnt == DRAM_CL)
						{
							if ((DRAM_CLK == true) && (!command_sent) && (!data_reserved))
							{
								if (((remained_banks_num < DRAM_rank_num*DRAM_bank_num) && (row_activate_cnt >= remained_banks_num)) ||
									((remained_banks_num >= DRAM_rank_num*DRAM_bank_num) && (row_activate_cnt == DRAM_rank_num*DRAM_bank_num)))
								{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read Command at Rank=" << hex << rank_sel << ", Bank=" << bank_sel << ", Col=0x" << col_addr << dec << endl;
#endif
									DRAM_port->SDRAM_read_command(1, rank_sel, bank_sel, col_addr, true);
									command_sent = true;
									data_reserved = true;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting CAS Latency...CL count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].CL_cnt << endl;
#endif
									bank_manager[i].CL_cnt--;
									bank_manager[i].read_auto_precharge_cnt--;
								}
							}
						}
						else if (bank_manager[i].CL_cnt != 0)
						{
							if (DRAM_CLK == true)
							{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting CAS Latency...CL count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].CL_cnt << endl;
#endif
								bank_manager[i].CL_cnt--;
							}
							if (bank_manager[i].read_auto_precharge_cnt != 0)
							{
								bank_manager[i].read_auto_precharge_cnt--;
								if (bank_manager[i].read_auto_precharge_cnt == 0)
									data_reserved = false;
							}
							else if ((DRAM_CLK == true) && (bank_manager[i].tRP_cnt != 0))
							{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
								bank_manager[i].tRP_cnt--;
							}
						}
						else
						{
							if (bank_manager[i].read_auto_precharge_cnt != 0)
							{
								bank_manager[i].read_auto_precharge_cnt--;
								if (bank_manager[i].read_auto_precharge_cnt == 0)
									data_reserved = false;
							}
							else if ((DRAM_CLK == true) && (bank_manager[i].tRP_cnt != 0))
							{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
								bank_manager[i].tRP_cnt--;
							}
							if (bank_manager[i].read_data_enable)
							{
								if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) != 'Z')
								{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Before Read data : 0x" << hex << SDRAM_current_data.upper_bytes << " | 0x" << SDRAM_current_data.lower_bytes << dec;
#endif
									SDRAM_current_data = DRAM_port->SDRAM_DDR_read_data(1, rank_sel, bank_sel, DDR_DEFAULT_BURST_LENGTH-bank_manager[i].BL_cnt, false);
									DRAM_WR_FIFO[(bank_manager[i].bank_addr[bank_manager[i].bank_first]-addr)/4+2*(DRAM_BL-bank_manager[i].BL_cnt)] = SDRAM_current_data.lower_bytes;
									DRAM_WR_FIFO[(bank_manager[i].bank_addr[bank_manager[i].bank_first]-addr)/4+2*(DRAM_BL-bank_manager[i].BL_cnt)+1] = SDRAM_current_data.upper_bytes;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
									cout << ", After Read data : 0x" << hex << SDRAM_current_data.upper_bytes << " | 0x" << SDRAM_current_data.lower_bytes << dec << endl;
#endif
									bank_manager[i].BL_cnt--;
									if (bank_manager[i].BL_cnt == 0)
									{
										bank_manager[i].CL_cnt = DRAM_CL;
										bank_manager[i].read_data_enable = false;
										bank_manager[i].BL_cnt = DRAM_BL;
										bank_manager[i].read_auto_precharge_cnt = DRAM_BL;
										if (bank_manager[i].tRP_cnt != 0)
										{
											bank_manager[i].bank_curr_state = SDRAM_PRECHARGE;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
											cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
											bank_manager[i].tRP_cnt--;
										}
										else
										{
											if (i == start_bank)
												start_bank = (start_bank+1)%(DRAM_rank_num*DRAM_bank_num);
											bank_manager[i].bank_first++;
											bank_manager[i].bank_curr_state=SDRAM_IDLE;
											bank_manager[i].tRP_cnt = DRAM_tRP;
											remained_banks_num--;
											if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
												row_activate_cnt--;
											if (remained_banks_num == 0)
											{
												unsigned data_finished = 0;
												for (int j=0;j<DRAM_rank_num*DRAM_bank_num;j++)
													if ((bank_manager[j].bank_curr_state == SDRAM_IDLE) && (bank_manager[j].bank_first>bank_manager[j].bank_last))
														data_finished++;
												if (data_finished == DRAM_rank_num*DRAM_bank_num)
												{
													if (!SSD_DMA_PRD_fetched)
													{
														SSD_DMA_base_addr = DRAM_WR_FIFO[0];
														SSD_DMA_base_cnt = DRAM_WR_FIFO[1] & 0xFFFF;
														SSD_DMA_PRD_start_addr = SSD_DMA_PRD_start_addr + 2*4;
														SSD_DMA_PRD_fetched = true;
														SSD_DMA_PRD_fetched_time = sc_time_stamp();
														SSD_DMA_LBA_current += SSD_DMA_base_cnt;
														PRD_current--;
														if (PRD_current != 0 && SSD_DMA_command == 0xC8)
															DRAM_Cache_RD_request = true;
//#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
														cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> PRD for North SSD-DMA is fetched" << endl;
//#endif
													}
													else if (!SSD_DMA_buffered)
													{
														LBA_write = SSD_DMA_LBA_current;
//#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
														cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to FIFO is finished...Enable Write to CACHE." <<endl;
//#endif
														SSD_DMA_buffered = true;
														SSD_DMA_buffered_time = sc_time_stamp();
														DRAM_Cache_WR_request = true;
													}
													else
													{
														cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> These data are not requested." << endl;
														sc_stop();
													}
													DRAM_curr_state = IDLE_STATE;
												}
											}
										}
									}
								}
							}
							else
							{
								bank_manager[i].read_data_enable = true;
							}
						}
						break;
					case SDRAM_PRECHARGE:
						if (DRAM_CLK == true)
						{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
							if (bank_manager[i].tRP_cnt != 0)
								bank_manager[i].tRP_cnt--;
							if(bank_manager[i].tRP_cnt == 0)
							{
								if (i == start_bank)
									start_bank = (start_bank+1)%(DRAM_rank_num*DRAM_bank_num);
								bank_manager[i].bank_first++;
								bank_manager[i].bank_curr_state=SDRAM_IDLE;
								bank_manager[i].tRP_cnt = DRAM_tRP;
								remained_banks_num--;
								if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
									row_activate_cnt--;
								if (remained_banks_num == 0)
								{
									unsigned data_finished = 0;
									for (int j=0;j<DRAM_rank_num*DRAM_bank_num;j++)
										if ((bank_manager[j].bank_curr_state == SDRAM_IDLE) && (bank_manager[j].bank_first>bank_manager[j].bank_last))
											data_finished++;
									if (data_finished == DRAM_rank_num*DRAM_bank_num)
									{
										if (!SSD_DMA_PRD_fetched)
										{
											SSD_DMA_base_addr = DRAM_WR_FIFO[0];
											SSD_DMA_base_cnt = DRAM_WR_FIFO[1] & 0xFFFF;
											SSD_DMA_PRD_start_addr = SSD_DMA_PRD_start_addr + 2*4;
											SSD_DMA_PRD_fetched = true;
											SSD_DMA_PRD_fetched_time = sc_time_stamp();
											SSD_DMA_LBA_current += SSD_DMA_base_cnt;
											PRD_current--;
											if (PRD_current != 0 && SSD_DMA_command == 0xC8)
												DRAM_Cache_RD_request = true;
//#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
											cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> PRD for North SSD-DMA is fetched" << endl;
//#endif
										}
										else if (!SSD_DMA_buffered)
										{
											LBA_write = SSD_DMA_LBA_current;
//#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
											cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to FIFO is finished...Enable Write to CACHE." <<endl;
//#endif
											SSD_DMA_buffered = true;
											SSD_DMA_buffered_time = sc_time_stamp();
											DRAM_Cache_WR_request = true;
										}
										else
										{
											cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> These data are not requested." << endl;
											sc_stop();
										}
										DRAM_curr_state = IDLE_STATE;
									}
								}
							}
						}
						break;
					default:
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Unknown State!" << endl << flush;
						sc_stop();
					}
				}
				bank_cnt++;
			}
			break;
		case WRITE_STATE:
			command_sent = false;
			bank_cnt = 0;
			for (int i=start_bank;bank_cnt<8;i=(i+1)%(DRAM_rank_num*DRAM_bank_num))
			{
				if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
				{
					unsigned rank_sel = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 5) & 0x1;
					unsigned bank_sel = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 6) & 0x3;
					SDRAM_addr_t row_addr = (bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 16) & 0x1FFF;
					SDRAM_addr_t col_addr = ((bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 6) & 0x3FC) | ((bank_manager[i].bank_addr[bank_manager[i].bank_first] >> 3) & 0x3);
					switch (bank_manager[i].bank_curr_state)
					{
					case SDRAM_IDLE:
						if ((DRAM_CLK == true) && (!command_sent) && (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == 'Z'))
						{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Row Activate at Rank=" << hex << rank_sel << ", Bank=" << bank_sel << ", Row=0x" << row_addr << dec << endl;
#endif
							DRAM_port->SDRAM_row_activate(1, rank_sel, bank_sel, row_addr);
							command_sent = true;
							row_activate_cnt++;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Row Activate...tRCD count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRCD_cnt << endl;
#endif
							bank_manager[i].bank_curr_state = SDRAM_ROW_ACT;
							bank_manager[i].tRCD_cnt--;
						}
						break;
					case SDRAM_ROW_ACT:
						if (DRAM_CLK == true)
						{
							if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) != 'Z')
							{
								bank_manager[i].bank_curr_state = SDRAM_IDLE;
								row_activate_cnt--;
								bank_manager[i].tRCD_cnt++;
							}
							else
							{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Row Activate...tRCD count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRCD_cnt << endl;
#endif
								bank_manager[i].tRCD_cnt--;
								if (bank_manager[i].tRCD_cnt == 0)
								{
									bank_manager[i].bank_curr_state = SDRAM_WRITE_AP;
									bank_manager[i].tRCD_cnt = DRAM_tRCD;
								}
							}
						}
						break;
					case SDRAM_WRITE_AP:
						if (bank_manager[i].tDQSS_cnt == DRAM_tDQSS)
						{
							if ((DRAM_CLK == true) && (!command_sent) && (!data_reserved))
							{
								if (((remained_banks_num < DRAM_rank_num*DRAM_bank_num) && (row_activate_cnt >= remained_banks_num)) ||
									((remained_banks_num >= DRAM_rank_num*DRAM_bank_num) && (row_activate_cnt == DRAM_rank_num*DRAM_bank_num)))
								{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write Command at Rank=" << hex << rank_sel << ", Bank=" << bank_sel << ", Col=0x" << col_addr << dec << endl;
#endif
									DRAM_port->SDRAM_write_command(1, rank_sel, bank_sel, col_addr, true);
									if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == 'Z')
									{
										DRAM_port->SDRAM_DDR_set_DQS(1, rank_sel, bank_sel, sc_logic('0'));
									}
									else
									{
										cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> DQS signal is not valid." << endl;
										sc_stop();
									}
									command_sent = true;
									data_reserved = true;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting DQS Transition...tDQSS count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tDQSS_cnt << endl;
#endif
									bank_manager[i].tDQSS_cnt--;
								}
							}
						}
						else
						{
							if (bank_manager[i].tDQSS_cnt != 0)
							{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
								cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting DQS Transition...tDQSS count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tDQSS_cnt << endl;
#endif
								bank_manager[i].tDQSS_cnt--;
							}
							else
							{
								if (bank_manager[i].BL_cnt != 0)
								{
									SDRAM_current_data.lower_bytes = DRAM_RD_FIFO[(bank_manager[i].bank_addr[bank_manager[i].bank_first]-addr)/4+2*(DRAM_BL-bank_manager[i].BL_cnt)];
									SDRAM_current_data.upper_bytes = DRAM_RD_FIFO[(bank_manager[i].bank_addr[bank_manager[i].bank_first]-addr)/4+2*(DRAM_BL-bank_manager[i].BL_cnt)+1];
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
									cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write data : 0x" << hex << SDRAM_current_data.upper_bytes << " | 0x" << SDRAM_current_data.lower_bytes << dec << endl;
#endif
									DRAM_port->SDRAM_DDR_write_data(1, rank_sel, bank_sel, DDR_DEFAULT_BURST_LENGTH-bank_manager[i].BL_cnt, SDRAM_current_data, false);
									if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == '0')
									{
										DRAM_port->SDRAM_DDR_set_DQS(1, rank_sel, bank_sel, sc_logic('1'));
									}
									else if (DRAM_port->SDRAM_DDR_get_DQS(1, rank_sel, bank_sel) == '1')
									{
										DRAM_port->SDRAM_DDR_set_DQS(1, rank_sel, bank_sel, sc_logic('0'));
									}
									else
									{
										cout << "ERROR @ " << sc_module::name() << "[" << sc_time_stamp() << "]> DQS signal is not valid." << endl;
										sc_stop();
									}
									bank_manager[i].BL_cnt--;
									if (bank_manager[i].BL_cnt == 0)
									{
										DRAM_port->SDRAM_DDR_set_DQS(1, rank_sel, bank_sel, sc_logic('Z'));
										data_reserved = false;
									}
								}
								if ((bank_manager[i].BL_cnt == 0) && (DRAM_CLK == true))
								{
									if (bank_manager[i].tWR_cnt != 0)
									{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
										cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tWR count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tWR_cnt << endl;
#endif
										bank_manager[i].tWR_cnt--;
									}
									else
									{
										bank_manager[i].bank_curr_state= SDRAM_PRECHARGE;
										bank_manager[i].BL_cnt = DRAM_BL;
										bank_manager[i].tDQSS_cnt = DRAM_tDQSS;
										bank_manager[i].tWR_cnt = DRAM_tWR;
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
										cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
										bank_manager[i].tRP_cnt--;
									}
								}
							}
						}
						break;
					case SDRAM_PRECHARGE:
						if (DRAM_CLK == true)
						{
#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Waiting Precharge...tRP count(" << rank_sel << ", " << bank_sel << ") : " << bank_manager[i].tRP_cnt << endl;
#endif
							bank_manager[i].tRP_cnt--;
							if(bank_manager[i].tRP_cnt==0)
							{
								if (i == start_bank)
									start_bank = (start_bank+1)%(DRAM_rank_num*DRAM_bank_num);
								bank_manager[i].bank_first++;
								bank_manager[i].bank_curr_state=SDRAM_IDLE;
								bank_manager[i].tRP_cnt = DRAM_tRP;
								remained_banks_num--;
								if (bank_manager[i].bank_first<=bank_manager[i].bank_last)
									row_activate_cnt--;
								if (remained_banks_num == 0)
								{
									unsigned data_finished = 0;
									for (int j=0;j<DRAM_rank_num*DRAM_bank_num;j++)
										if ((bank_manager[j].bank_curr_state == SDRAM_IDLE) && (bank_manager[j].bank_first>bank_manager[j].bank_last))
											data_finished++;
									if (data_finished == DRAM_rank_num*DRAM_bank_num)
									{
										SSD_DMA_PRD_fetched = false;
										SSD_DMA_buffered = false;
//#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
										cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Write to Memory complete." << endl;
//#endif
										if (PRD_current == 0)
										{
											SSD_DMA_enable = false;
											SSD_DMA_end = 0x1;
//#ifdef	DEBUG_DDR_SSD_SDRAM_CTRL
											cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> North SSD-DMA is finished" << endl;
//#endif
										}
										DRAM_curr_state = IDLE_STATE;
									}
								}
							}
						}
						break;
					default:
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Unknown State!" << endl << flush;
						sc_stop();
					}
				}
				bank_cnt++;
			}
			break;
		case EMRS_STATE:
			if (DRAM_CLK == true)
				DRAM_curr_state = IDLE_STATE;
			break;
		case REFRESH_STATE:
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Unsupported State for this port!" << endl << flush;
			sc_stop();
			break;
		default:
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> " << "Unknown State!" << endl << flush;
			sc_stop();
		}
		wait();
	}
}

void
DDR_SSD::Cache_arbiter_action()
{
	while (true)
	{
		if ((SSD_Cache_WR_request || DRAM_Cache_WR_request) && (SSD_Cache_finished) && (DRAM_Cache_finished))
		{
#ifdef	DEBUG_DDR_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Start Write to CACHE." <<endl;
#endif
			C_WRITE = true;
			if (DRAM_Cache_WR_request)
			{
				DRAM_or_SSD = true;
				LBA_write = SSD_DMA_LBA_current;
				DRAM_Cache_WR_request = false;
				DRAM_Cache_finished = false;
			}
			else
			{
				DRAM_or_SSD = false;
				LBA_write = LBA_write_temp;
				SSD_Cache_WR_request = false;
				SSD_Cache_finished = false;
			}
		}
		else if ((SSD_Cache_RD_request || DRAM_Cache_RD_request) && (SSD_Cache_finished) && (DRAM_Cache_finished))
		{
//#ifdef	DEBUG_DDR_SSD
			cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Read started." <<endl;
//#endif
			C_READ = true;
			if (DRAM_Cache_RD_request)
			{
				DRAM_or_SSD = true;
				LBA_read = SSD_DMA_LBA_current;
				DRAM_Cache_RD_request = false;
				DRAM_Cache_finished = false;
			}
			else
			{
				DRAM_or_SSD = false;
				LBA_read = LBA_read_temp;
				SSD_Cache_RD_request = false;
				SSD_Cache_finished = false;
			}
		}
		wait();
	}
}

const bool
DDR_SSD::Cache_decision(unsigned	LBA_) const
{
	if (decision[LBA_%(CACHE_BUFFER/CRC_BUFFER)] == LBA_)
		return true;
	else
		return false;
}

void
DDR_SSD::Cache_process()
{
	while (true)
	{
		if (C_WRITE)
		{
			if (((!DRAM_or_SSD) && (SSD_WR_pOUT < CRC_BUFFER)) || ((DRAM_or_SSD) && (DRAM_WR_pOUT < CRC_BUFFER)))
			{
				if (!DRAM_or_SSD)
				{
#ifdef	DEBUG_DDR_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to CACHE(" << SSD_WR_pOUT << ") : 0x" << hex << SSD_WR_FIFO[SSD_WR_pOUT] << dec << endl;
#endif
					SRAM_Array[(LBA_write%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+SSD_WR_pOUT] = SSD_WR_FIFO[SSD_WR_pOUT];
					SSD_WR_pOUT++;
				}
				else
				{
#ifdef	DEBUG_DDR_SSD
					cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to CACHE(" << DRAM_WR_pOUT << ") : 0x" << hex << DRAM_WR_FIFO[DRAM_WR_pOUT] << dec << endl;
#endif
					SRAM_Array[(LBA_write%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+DRAM_WR_pOUT] = DRAM_WR_FIFO[DRAM_WR_pOUT];
					DRAM_WR_pOUT++;
				}
			}
			else
			{
				if (LBA_write < (NAND_CH2_START_LBA<<12))
				{
					if (CH1_Buffer_pIN < CRC_BUFFER)
					{
						CH1_Buffer[CH1_Buffer_pIN] = SRAM_Array[(LBA_write%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+CH1_Buffer_pIN];
#ifdef	DEBUG_DDR_SSD
						cout<<"["<<sc_time_stamp()<<"] CACHE_process> Write to CH1_Buffer : "<< CH1_Buffer_pIN << endl;
#endif
						CH1_Buffer_pIN++;
					}
					else if (current_state_1 == T_IDLE)
					{
#ifdef	DEBUG_DDR_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to CACHE & CH1_BUFFER is finished." << endl;
#endif
						decision[LBA_write%(CACHE_BUFFER/CRC_BUFFER)] = LBA_write;
						C_WRITE = false;
						if (!DRAM_or_SSD)
							SSD_WR_pOUT = 0;
						else
							DRAM_WR_pOUT = 0;
						F_WRITE_1 = true;
#ifdef	DEBUG_DDR_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to Flash CH1 is being requested." <<endl;
#endif
						CH1_Buffer_pIN = 0;
					}
				}
				else
				{
					if (CH2_Buffer_pIN < CRC_BUFFER)
					{
						CH2_Buffer[CH2_Buffer_pIN] = SRAM_Array[(LBA_write%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+CH2_Buffer_pIN];
#ifdef	DEBUG_DDR_SSD
						cout<<"["<<sc_time_stamp()<<"] CACHE_process> Write to CH2_Buffer : "<< CH2_Buffer_pIN << endl;
#endif
						CH2_Buffer_pIN++;
					}
					else if (current_state_2 == T_IDLE)
					{
#ifdef	DEBUG_DDR_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to CACHE & CH2_BUFFER is finished." << endl;
#endif
						decision[LBA_write%(CACHE_BUFFER/CRC_BUFFER)] = LBA_write;
						C_WRITE = false;
						if (!DRAM_or_SSD)
							SSD_WR_pOUT = 0;
						else
							DRAM_WR_pOUT = 0;
						F_WRITE_2 = true;
#ifdef	DEBUG_DDR_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Write to Flash CH2 is being requested." <<endl;
#endif
						CH2_Buffer_pIN = 0;
					}
				}
			}
		}
		if (C_READ)
		{
			if (Cache_decision(LBA_read))
//			if (true)
			{
				if (!DRAM_or_SSD) 
				{
					if (SSD_RD_pIN < CRC_BUFFER)
					{
//#ifdef	DEBUG_DDR_SSD
						if (SSD_RD_pIN == 0)
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD I/F Read is started." <<endl;
//#endif
						SSD_RD_FIFO[SSD_RD_pIN] = SRAM_Array[(LBA_read%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+SSD_RD_pIN];
#ifdef	DEBUG_DDR_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read from CACHE(" << SSD_RD_pIN << ") : 0x" << hex << SSD_RD_FIFO[SSD_RD_pIN] << dec << endl;
#endif
						SSD_RD_pIN++;
						if (SSD_RD_pIN == CRC_BUFFER/2+1)
						{
#ifdef	DEBUG_DDR_SSD
							cout<<"["<<sc_time_stamp()<<"] CACHE_process> Read FIFO is ready...SSD_DQS become 0" << endl;
#endif
							SSD_DQS = '0';
						}
					}
					else
					{
						C_READ = false;
						SSD_Cache_finished = true;
						SSD_RD_pIN = 0;
					}
				}
				else
				{
					if (DRAM_RD_pIN < CRC_BUFFER)
					{
						DRAM_RD_FIFO[DRAM_RD_pIN] = SRAM_Array[(LBA_read%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+DRAM_RD_pIN];
#ifdef	DEBUG_DDR_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read from CACHE(" << DRAM_RD_pIN << ") : 0x" << hex << DRAM_RD_FIFO[DRAM_RD_pIN] << dec << endl;
#endif
						DRAM_RD_pIN++;
					}
					else
					{
						C_READ = false;
						SSD_DMA_buffered = true;
						DRAM_Cache_finished = true;
						DRAM_RD_pIN = 0;
					}
				}
			}
			else
			{
				if (LBA_read < NAND_CH2_START_LBA<<12)
				{
					if (cache_buffer_read_1)
					{
						if (CH1_Buffer_pOUT < CRC_BUFFER)
						{
							SRAM_Array[(LBA_read%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+CH1_Buffer_pOUT] = CH1_Buffer[CH1_Buffer_pOUT];
#ifdef	DEBUG_DDR_SSD
							cout<<"["<<sc_time_stamp()<<"] CACHE_process> READ from CH1_Buffer : "<< CH1_Buffer_pOUT << endl;
#endif
							CH1_Buffer_pOUT++;
						}
						else
						{
							decision[LBA_read%(CACHE_BUFFER/CRC_BUFFER)] = LBA_read;
							CH1_Buffer_pOUT = 0;
							cache_buffer_read_1 = false;
//#ifdef	DEBUG_SATA
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Read finished"<<endl;
//#endif
						}
					}
					else
					{
						if ((!F_WRITE_1) && (current_state_1 == T_IDLE))
						{
							F_READ_1 = true;
#ifdef	DEBUG_DDR_SSD
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read from Flash CH1 is being requested." <<endl;
#endif
						}
					}
				}
				else
				{
					if (cache_buffer_read_2)
					{
						if (CH2_Buffer_pOUT < CRC_BUFFER)
						{
							SRAM_Array[(LBA_read%(CACHE_BUFFER/CRC_BUFFER))*CRC_BUFFER+CH2_Buffer_pOUT] = CH2_Buffer[CH2_Buffer_pOUT];
#ifdef	DEBUG_DDR_SSD
							cout<<"["<<sc_time_stamp()<<"] CACHE_process> READ from CH2_Buffer : "<< CH2_Buffer_pOUT << endl;
#endif
							CH2_Buffer_pOUT++;
						}
						else
						{
							decision[LBA_read%(CACHE_BUFFER/CRC_BUFFER)] = LBA_read;
							CH2_Buffer_pOUT = 0;
							cache_buffer_read_2 = false;
//#ifdef	DEBUG_DDR_SSD
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> SSD Internal Read finished"<<endl;
//#endif
						}
					}
					else
					{
						if ((!F_WRITE_2) && (current_state_2 == T_IDLE))
						{
							F_READ_2 = true;
#ifdef	DEBUG_DDR_SSD
							cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> Read from Flash CH2 is being requested." <<endl;
#endif
						}
					}
				}
			}
		}
		wait();
	}
}

void
DDR_SSD::FLASH_addr_gen(unsigned	LBA_)
{
	PA = LBA_;
	BA = LBA_ >> 6;
	
	col_addr0 = 0;
	col_addr1 = 0;
	row_addr0 = ((BA << 6) & 0x3) | PA;
	row_addr1 = BA >> 2 & 0xFF;
}

void
DDR_SSD::FLASHcontroller_process_1()
{
	while (true)
	{
		switch(current_state_1)
		{
		case T_IDLE:
			if (F_WRITE_1 || F_READ_1 || F_ERASE_1)
			{
#ifdef	DEBUG_DDR_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_IDLE"<<endl;
#endif
				current_state_1 = T_COMMAND;
			}
			break;
		case T_COMMAND:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_COMMAND"<<endl;
#endif
			if (F_WRITE_1)
			{ 
				FLASH_addr_gen(LBA_write);
				cmd_1 = NAND_START_PROGRAM;
				F_WRITE_1 = false;
				current_state_1 = T_COL_ADDR0;
			}
			else if (F_READ_1)
			{
				FLASH_addr_gen(LBA_read);
				cmd_1 = NAND_START_READ;
				F_READ_1 = false;
				current_state_1 = T_COL_ADDR0;
			}
			else if (F_ERASE_1)
			{
				cmd_1 = NAND_START_ERASE;
				F_ERASE_1 = false;
				current_state_1 = T_ROW_ADDR0;
			}
			else
			{
				cout << "[" << sc_time_stamp() << "] FLASH_CTRL_1> Unsupported command!" << endl;
				sc_stop();
			}
			NAND_port_1->NAND_write(cmd_1);
			break;
		case T_COL_ADDR0:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_COL_ADDR0"<<endl;
#endif
			current_state_1 = T_COL_ADDR1;
			NAND_port_1->NAND_write(col_addr0);
			break;
		case T_COL_ADDR1:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_COL_ADDR1"<<endl;
#endif
			current_state_1 = T_ROW_ADDR0;
			NAND_port_1->NAND_write(col_addr1);
			break;
		case T_ROW_ADDR0:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_ROW_ADDR0"<<endl;
#endif
			current_state_1 = T_ROW_ADDR1;
			NAND_port_1->NAND_write(row_addr0);
			break;
		case T_ROW_ADDR1:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_ROW_ADDR1"<<endl;
#endif
			NAND_port_1->NAND_write(row_addr1);
			if (cmd_1 == NAND_START_PROGRAM)
				current_state_1 = T_WR_DATA;
			else if (cmd_1 == NAND_START_READ)
				current_state_1 = T_CONFIRM;
			else if (cmd_1 == NAND_START_ERASE)
				current_state_1 = T_CONFIRM;
			break;
		case T_WR_DATA:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_WR_DATA"<<endl;
#endif
			NAND_port_1->NAND_write((byte)(CH1_Buffer[DATA_cnt_1/4] >> ((DATA_cnt_1%4)*8)));
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> Write to NAND : 0x" << hex << (unsigned)((byte)(CH1_Buffer[DATA_cnt_1/4] >> ((DATA_cnt_1%4)*8))) << dec <<endl; 
#endif
			DATA_cnt_1++;
			if (DATA_cnt_1 == NAND_PAGE_SIZE)
			{
				current_state_1 = T_CONFIRM;
				DATA_cnt_1 = 0;
			}
			break;
		case T_RD_DATA:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_RD_DATA"<<endl;
#endif
			DATA_word_1 = DATA_word_1 | NAND_port_1->NAND_read() << (DATA_cnt_1%4)*8;
			if ((DATA_cnt_1%4) == 3)
			{
				CH1_Buffer[DATA_cnt_1/4] = DATA_word_1;
#ifdef	DEBUG_DDR_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> CH1_Buffer["<< DATA_cnt_1/4 <<"] : 0x" << hex << DATA_word_1 << dec <<endl; 
#endif
				DATA_word_1 = 0;
			}
			DATA_cnt_1++;
			if (DATA_cnt_1 == NAND_PAGE_SIZE)
			{
				NAND_port_1->NAND_write(NAND_HI_Z);
				cache_buffer_read_1 = true;
				DATA_cnt_1 = 0;
				current_state_1 = T_IDLE;
			}
			break;
		case T_CONFIRM:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_CONFIRM"<<endl;
#endif
			if (cmd_1 == NAND_START_PROGRAM)
			{
				if (!Confirm_commanded_1)
				{
					NAND_port_1->NAND_write(NAND_END_PROGRAM);
					Confirm_commanded_1 = true;
				}
				else if (NAND_port_1->NAND_read_R_B())
				{
					Confirm_commanded_1 = false;
					current_state_1 = T_READ_STATUS;
				}
			}
			else if (cmd_1 == NAND_START_READ)
			{
				if (!Confirm_commanded_1)
				{
					NAND_port_1->NAND_write(NAND_END_READ);
					Confirm_commanded_1 = true;
				}
				else if (NAND_port_1->NAND_read_R_B())
				{
					Confirm_commanded_1 = false;
					current_state_1 = T_RD_DATA;
				}
			}
			else if (cmd_1 == NAND_START_ERASE)
			{
				NAND_port_1->NAND_write(NAND_END_ERASE);
				current_state_1 = T_READ_STATUS;
			}
			break;
		case T_READ_STATUS:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> T_READ_STATUS"<<endl;
#endif
			NAND_port_1->NAND_write(NAND_READ_STATUS);
			current_state_1 = IO_CHECK;
			break;
		case IO_CHECK:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> IO_CHECK"<<endl;
#endif
			if (NAND_port_1->NAND_read() == NAND_IO_OK)
			{
#ifdef	DEBUG_DDR_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_1> IO_CHECK RESULT : OK" << endl;
#endif
				current_state_1 = T_IDLE;
				if (cmd_1 == NAND_START_PROGRAM)
				{
					if (!DRAM_or_SSD)
						SSD_Cache_finished = true;
					else
						DRAM_Cache_finished = true;
					SSD_DMA_PRD_fetched = false;
					SSD_DMA_buffered = false;
					SSD_write_end = 0x1;
					SSD_DMA_LBA_current += SSD_DMA_base_cnt;
//					PRD_current--;
					if (PRD_current == 0)
					{
						SSD_DMA_enable = false;
						SSD_DMA_end = 0x1;
//#ifdef	DEBUG_DDR_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> North SSD-DMA is finished" << endl;
//#endif
					}
				}
			}
			break;
		default:
			;
		}
		wait();
	}		
}

void
DDR_SSD::FLASHcontroller_process_2()
{
	while (true)
	{
		switch(current_state_2)
		{
		case T_IDLE:
			if (F_WRITE_2 || F_READ_2 || F_ERASE_2)
			{
#ifdef	DEBUG_DDR_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_IDLE"<<endl;
#endif
				current_state_2 = T_COMMAND;
			}
			break;
		case T_COMMAND:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_COMMAND"<<endl;
#endif
			if (F_WRITE_2)
			{ 
				FLASH_addr_gen(LBA_write);
				cmd_2 = NAND_START_PROGRAM;
				F_WRITE_2 = false;
				current_state_2 = T_COL_ADDR0;
			}
			else if (F_READ_2)
			{
				FLASH_addr_gen(LBA_read);
				cmd_2 = NAND_START_READ;
				F_READ_2 = false;
				current_state_2 = T_COL_ADDR0;
			}
			else if (F_ERASE_2)
			{
				cmd_2 = NAND_START_ERASE;
				F_ERASE_2 = false;
				current_state_2 = T_ROW_ADDR0;
			}
			else
			{
				cout << "[" << sc_time_stamp() << "] FLASH_CTRL_2> Unsupported command!" << endl;
				sc_stop();
			}
			NAND_port_2->NAND_write(cmd_2);
			break;
		case T_COL_ADDR0:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_COL_ADDR0"<<endl;
#endif
			current_state_2 = T_COL_ADDR1;
			NAND_port_2->NAND_write(col_addr0);
			break;
		case T_COL_ADDR1:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_COL_ADDR1"<<endl;
#endif
			current_state_2 = T_ROW_ADDR0;
			NAND_port_2->NAND_write(col_addr1);
			break;
		case T_ROW_ADDR0:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_ROW_ADDR0"<<endl;
#endif
			current_state_2 = T_ROW_ADDR1;
			NAND_port_2->NAND_write(row_addr0);
			break;
		case T_ROW_ADDR1:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_ROW_ADDR1"<<endl;
#endif
			NAND_port_2->NAND_write(row_addr1);
			if (cmd_2 == NAND_START_ERASE)
				current_state_2 = T_WR_DATA;
			else if (cmd_2 == NAND_START_READ)
				current_state_2 = T_CONFIRM;
			else if (cmd_2 == NAND_START_ERASE)
				current_state_2 = T_CONFIRM;
			break;
		case T_WR_DATA:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_WR_DATA"<<endl;
#endif
			NAND_port_2->NAND_write((byte)(CH1_Buffer[DATA_cnt_2/4] >> ((DATA_cnt_2%4)*8)));
			DATA_cnt_2++;
			if (DATA_cnt_2 == NAND_PAGE_SIZE)
			{
				current_state_2 = T_CONFIRM; 
				DATA_cnt_2 = 0;
			}
			break;
		case T_RD_DATA:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_RD_DATA"<<endl;
#endif
			DATA_word_2 = DATA_word_2 | NAND_port_2->NAND_read() << (DATA_cnt_2%4)*8;
			if ((DATA_cnt_2%4) == 3)
			{
				CH1_Buffer[DATA_cnt_2/4] = DATA_word_2;
#ifdef	DEBUG_DDR_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> CH1_Buffer["<< DATA_cnt_2/4 <<"] : 0x" << hex << DATA_word_2 << dec << endl; 
#endif
				DATA_word_2 = 0;
			}
			DATA_cnt_2++;
			if (DATA_cnt_2 == NAND_PAGE_SIZE)
			{
				cache_buffer_read_2 = true;
				DATA_cnt_2 = 0;
				current_state_2 = T_IDLE;
			}
			break;
		case T_CONFIRM:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_CONFIRM"<<endl;
#endif
			if (cmd_2 == NAND_START_PROGRAM)
			{
				if (!Confirm_commanded_2)
				{
					NAND_port_2->NAND_write(NAND_END_PROGRAM);
					Confirm_commanded_2 = true;
				}
				else if (NAND_port_2->NAND_read_R_B())
				{
					Confirm_commanded_2 = false;
					current_state_2 = T_READ_STATUS;
				}
			}
			else if (cmd_2 == NAND_START_READ)
			{
				if (!Confirm_commanded_2)
				{
					NAND_port_2->NAND_write(NAND_END_READ);
					Confirm_commanded_2 = true;
				}
				else if (NAND_port_2->NAND_read_R_B())
				{
					Confirm_commanded_2 = false;
					current_state_2 = T_RD_DATA;
				}
			}
			else if (cmd_2 == NAND_START_ERASE)
			{
				NAND_port_2->NAND_write(NAND_END_ERASE);
				current_state_2 = T_READ_STATUS;
			}
			break;
		case T_READ_STATUS:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> T_READ_STATUS"<<endl;
#endif
			NAND_port_2->NAND_write(NAND_READ_STATUS);
			current_state_2 = IO_CHECK;
			break;
		case IO_CHECK:
#ifdef	DEBUG_DDR_SSD
			cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> IO_CHECK"<<endl;
#endif
			if (NAND_port_2->NAND_read() == NAND_IO_OK)
			{
#ifdef	DEBUG_DDR_SSD
				cout<<"["<<sc_time_stamp()<<"] FLASH_CTRL_2> IO_CHECK RESULT : OK" << endl;
#endif
				current_state_2 = T_IDLE;
				if (cmd_2 == NAND_START_PROGRAM)
				{
					if (!DRAM_or_SSD)
						SSD_Cache_finished = true;
					else
						DRAM_Cache_finished = true;
					SSD_DMA_PRD_fetched = false;
					SSD_DMA_buffered = false;
					SSD_write_end = 0x1;
					SSD_DMA_LBA_current += SSD_DMA_base_cnt;
//					PRD_current--;
					if (PRD_current == 0)
					{
						SSD_DMA_enable = false;
						SSD_DMA_end = 0x1;
//#ifdef	DEBUG_DDR_SSD
						cout << "[" << sc_time_stamp() << "] " << sc_module::name() << "> North SSD-DMA is finished" << endl;
//#endif
					}
				}
			}
			break;
		default:
			;
		}
		wait();
	}		
}